package icasue.net.socket.utils;

import icasue.net.base.annotations.NotNull;
import icasue.net.base.annotations.Nullable;

import java.lang.invoke.MethodHandles;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * @Author: Qiao Hang
 * @CreateDate: 2021/5/18 下午5:45
 * @UpdateDate:
 * @Description:
 */
public class Refection {
    private static final int ALLOWED_MODES = MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED
            | MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC;
    public static final MethodHandles.Lookup methodLookup;
    public static final Class currentLookUpClass;

    static {
        //get lookup
        methodLookup = reFindLookUp(MethodHandles.Lookup.class);
        currentLookUpClass = MethodHandles.Lookup.class;
    }

    private static MethodHandles.Lookup reFindLookUp(Class cls){
        //get lookup
        MethodHandles.Lookup lookupTemp = null;
        try {
            //java 9
            Method lookupMethod = MethodHandles.class.getDeclaredMethod("privateLookupIn", Class.class, MethodHandles.Lookup.class);
            lookupMethod.setAccessible(true);
            MethodHandles.Lookup lookup =
                    (MethodHandles.Lookup)lookupMethod.invoke(null, MethodHandles.Lookup.class, MethodHandles.lookup());
            lookupTemp = lookup;
        }catch (Exception e){
            //java 8
            try {
                Constructor<MethodHandles.Lookup> lookupConstruct = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class);
                lookupConstruct.setAccessible(true);
                lookupTemp = lookupConstruct.newInstance(cls, ALLOWED_MODES);
            } catch (Exception e1) {
                throw new RuntimeException(e1.getCause());
            }
        }
        return lookupTemp;
    }



    public static void fieldSet(@NotNull Class type, @NotNull String fName, @Nullable Object inst, @Nullable Object val){
        try {
            Field targetField = type.getDeclaredField(fName);
            targetField.setAccessible(true);
            targetField.set(inst,val);
        }catch (Throwable e){
            throw new RuntimeException("Refection :: fieldSet ::" + e.getMessage());
        }
    }

    public static Object fieldGet(@NotNull Class type, @NotNull String fName, @Nullable Object inst){
        try {
            Field targetField = type.getDeclaredField(fName);
            targetField.setAccessible(true);
            return targetField.get(inst);
        }catch (Throwable e){
            throw new RuntimeException("Refection :: fieldGet ::" + e.getMessage());
        }
    }

    public static Method findDeclareMethod(@NotNull Class type, @NotNull String fName, @Nullable Class... pTypes){
        try {
            Method targetMethod = type.getDeclaredMethod(fName,pTypes == null ? new Class[0] : pTypes);
            targetMethod.setAccessible(true);
            return targetMethod;
        }catch (Throwable e){
            throw new RuntimeException("Refection :: findDeclareMethod ::" + e.getMessage());
        }
    }

    public static Object methodInvoke(@NotNull Method method, @Nullable Object inst, @Nullable Object... params){
        try {
            method.setAccessible(true);
            return method.invoke(inst,params == null ? new Object[0] : params);
        }catch (Throwable e){
            throw new RuntimeException("Refection :: methodInvoke ::" + e.getMessage());
        }
    }

}
